/* 
 * Legal Notice 
 * 
 * This document and associated source code (the "Work") is a part of a 
 * benchmark specification maintained by the TPC. 
 * 
 * The TPC reserves all right, title, and interest to the Work as provided 
 * under U.S. and international laws, including without limitation all patent 
 * and trademark rights therein. 
 * 
 * No Warranty 
 * 
 * 1.1 TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE INFORMATION 
 *     CONTAINED HEREIN IS PROVIDED "AS IS" AND WITH ALL FAULTS, AND THE 
 *     AUTHORS AND DEVELOPERS OF THE WORK HEREBY DISCLAIM ALL OTHER 
 *     WARRANTIES AND CONDITIONS, EITHER EXPRESS, IMPLIED OR STATUTORY, 
 *     INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES, 
 *     DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR 
 *     PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF 
 *     WORKMANLIKE EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE. 
 *     ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, 
 *     QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT 
 *     WITH REGARD TO THE WORK. 
 * 1.2 IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THE WORK BE LIABLE TO 
 *     ANY OTHER PARTY FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO THE 
 *     COST OF PROCURING SUBSTITUTE GOODS OR SERVICES, LOST PROFITS, LOSS 
 *     OF USE, LOSS OF DATA, OR ANY INCIDENTAL, CONSEQUENTIAL, DIRECT, 
 *     INDIRECT, OR SPECIAL DAMAGES WHETHER UNDER CONTRACT, TORT, WARRANTY,
 *     OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS OR ANY OTHER AGREEMENT 
 *     RELATING TO THE WORK, WHETHER OR NOT SUCH AUTHOR OR DEVELOPER HAD 
 *     ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES. 
 * 
 * Contributors:
 * Gradient Systems
 */ 
/*
 * parameter handling functions
 */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "config.h"
#include "porting.h"
#include "r_params.h"
#include "tdefs.h"
#include "release.h"

#define PARAM_MAX_LEN	80

#ifndef TEST
extern option_t options[];
extern char *params[];
#else
option_t options[] =
{
{"PROG", OPT_STR|OPT_HIDE, 0, NULL, NULL, "tester"}, 
{"PARAMS", OPT_STR, 1, "read parameters from file <s>", read_file, ""}, 
{"DISTRIBUTIONS", OPT_STR, 2, "read distributions from file <s>", NULL, "tester_dist.idx"}, 
{"OUTDIR", OPT_STR, 3, "generate files in directory <s>", NULL, "./"}, 
{"VERBOSE", OPT_FLG, 4, "enable verbose output", NULL, "N"}, 
{"HELP", OPT_FLG, 5, "display this message", usage, "N"}, 
{"scale", OPT_INT, 6, "set scale to <i>", NULL, "1"}, 
NULL
};
char *params[9];
#endif

static int param_init = 0;

#define MAX_LINE_LEN	120
#ifdef WIN32
#define OPTION_START '/'
#else
#define OPTION_START '-'
#endif

int read_file(char *param_name, char *option);
int fnd_param(char *name);
void  print_params(void);

/*
 * Routine:  load_params()
 * Purpose:  
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: 
 * 20010621 JMS shared memory not yet implemented
 */
void
load_params()
{
/*
	int i=0;
	while (options[i].name != NULL)
	{
		load_param(i, GetSharedMemoryParam(options[i].index));
		i++;
	}
	SetSharedMemoryStat(STAT_ROWCOUNT, get_int("STEP"), 0);
*/
	return;
}

static int cacheChild = 0;
static int cacheFilter = 0;
/*
 * Routine:  set_flag(int f)
 * Purpose:  set a toggle parameter
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
void
set_flg(char *flag)
{
	int nParam;

	init_params();
	nParam = fnd_param(flag);
	if (nParam >= 0)
		strcpy(params[options[nParam].index], "Y");
	if (!strcasecmp(flag, "FILTER")) cacheFilter = 1;
	if (!strcasecmp(flag, "_CHILD_ONLY")) cacheChild = 1;
	return;
}

/*
 * Routine: clr_flg(f)
 * Purpose: clear a toggle parameter
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
void
clr_flg(char *flag)
{
	int nParam;

	init_params();
	nParam = fnd_param(flag);
	if (nParam >= 0)
		strcpy(params[options[nParam].index], "N");
	if (!strcasecmp(flag, "FILTER")) cacheFilter = 0;
	if (!strcasecmp(flag, "_CHILD_ONLY")) cacheChild = 0;
	return;
}

/*
 * Routine: is_set(int f)
 * Purpose: return the state of a toggle parameter, or whether or not a string or int parameter
 *          has been set
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
is_set(char *flag)
{
   int nParam,
      bIsSet = 0;
   
   init_params();
   nParam = fnd_param(flag);
   if (nParam >= 0)
   {
      if ((options[nParam].flags & TYPE_MASK) == OPT_FLG)
         bIsSet = (params[options[nParam].index][0] == 'Y')?1:0;
      else
         bIsSet = (options[nParam].flags & OPT_SET) || (strlen(options[nParam].dflt) > 0);
   }
   
   return(bIsSet );	/* better a false negative than a false positive ? */
}
int is_set_filter(void) { return cacheFilter; }
int is_set_child(void) { return cacheChild; }
/*
 * Routine: set_int(int var, char *value)
 * Purpose: set an integer parameter
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
void 
set_int(char *var, char *val)
{
	int nParam;

	init_params();
	nParam = fnd_param(var);
	if (nParam >= 0)
	{
		strcpy(params[options[nParam].index], val);
		options[nParam].flags |= OPT_SET;
	}
	return;
}

/*
 * Routine: get_int(char *var)
 * Purpose: return the value of an integer parameter
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int 
get_int(char *var)
{
	int nParam;

	init_params();
	nParam = fnd_param(var);
	if (nParam >= 0)
		return(atoi(params[options[nParam].index]));
	else
		return(0);
}

/*
 * Routine: set_str(int var, char *value)
 * Purpose: set a character parameter
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
void 
set_str(char *var, char *val)
{
	int nParam;
	
	init_params();
	nParam = fnd_param(var);
	if (nParam >= 0)
	{
		strcpy(params[options[nParam].index], val);
		options[nParam].flags |= OPT_SET;
	}


	return;
}

/*
 * Routine: get_str(char * var)
 * Purpose: return the value of a character parameter
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
char *
get_str(char *var)
{
	int nParam;

	init_params();
	nParam = fnd_param(var);
	if (nParam >= 0)
		return(params[options[nParam].index]);
	else
		return(NULL);
}

/*
 * Routine: init_params(void)
 * Purpose: initialize a parameter set, setting default values
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
init_params(void)
{
	int i;
	static int init = 0;

	if (init)
		return(0);


	for (i=0; options[i].name != NULL; i++)
	{
		params[options[i].index] = (char *)malloc(PARAM_MAX_LEN * sizeof(char));
		MALLOC_CHECK(params[options[i].index]);
		strncpy(params[options[i].index], options[i].dflt, 80);
		if (*options[i].dflt)
			options[i].flags |= OPT_DFLT;
	}

	init = 1;
	param_init = 1;
	
	return(0);
}

/*
 * Routine: print_options(struct OPTION_T *o, int file, int depth)
 * Purpose: print a summary of options
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
static void
print_options(struct OPTION_T *o, int bShowOptional)
{
	int i,
	w_adjust,
	bShow = 0,
	nCount = 0;

	for (i=0; options[i].name != NULL; i++)
	{
		/*
		 * options come in two groups, general and "hidden". Decide which group to show 
		 * in this pass, and ignore others
		 */
		bShow = 0;
		if (bShowOptional && (o[i].flags & OPT_ADV))
			bShow = 1;
		if (!bShowOptional && !(o[i].flags & OPT_ADV))
			bShow = 1;

		if (!bShow || (o[i].flags & OPT_HIDE))
			continue;

		nCount += 1;
		printf("%s = ", o[i].name);
		w_adjust = 15 - strlen(o[i].name);
		if (o[i].flags & OPT_INT)
			printf(" <n>   ");
		else if (o[i].flags & OPT_STR)
			printf(" <s>   ");
		else if (o[i].flags & OPT_SUB)
			printf(" <opt> ");
		else if (o[i].flags & OPT_FLG)
			printf(" [Y|N] ");
		else printf("       ");
		printf("%*s-- %s", w_adjust, " ", o[i].usage);
		if (o[i].flags & OPT_NOP)
			printf(" NOT IMPLEMENTED");
		printf("\n");
	}

	if (nCount == 0)
		printf("None defined.\n");

	return;
}
/*
 * Routine: save_file(char *path)
 * Purpose: print a summary of options
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
save_file(char *path)
{
	int i,
	w_adjust;
	FILE *ofp;
	time_t timestamp;

	init_params();
	time(&timestamp);
	
	if ((ofp = fopen(path, "w")) == NULL)
		return(-1);

	fprintf(ofp, "--\n-- %s Benchmark Parameter File\n-- Created: %s",
		get_str("PROG"), ctime(&timestamp));
	fprintf(ofp, "--\n-- Each entry is of the form: '<parameter> = <value> -- optional comment'\n");
	fprintf(ofp, "-- Refer to benchmark documentation for more details\n--\n");
	
	for (i=0; options[i].name != NULL; i++)
	{
		if (options[i].flags & OPT_HIDE)		/* hidden option */
			continue;
		if (strlen(params[options[i].index]) == 0)
			continue;

		fprintf(ofp, "%s = ", options[i].name);
		w_adjust = strlen(options[i].name) + 3;
		if (options[i].flags & OPT_STR)
		{
			fprintf(ofp, "\"%s\"", params[options[i].index]);
			w_adjust += 2;
		}
		else
			fprintf(ofp, "%s", params[options[i].index]);
		w_adjust += strlen(params[options[i].index]) + 3;
		w_adjust = 60 - w_adjust;
		fprintf(ofp, "%*s-- %s", w_adjust, " ", options[i].usage);
		if (options[i].flags & OPT_NOP)
			fprintf(ofp, " NOT IMPLEMENTED");
		fprintf(ofp, "\n");
	}

	fclose(ofp);

	return(0);
}

/*
 * Routine: usage(char *param_name, char *msg)
 * Purpose: display a usage message, with an optional error message
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
usage (char *param_name, char *msg)
{
	init_params();
	
	fprintf (stderr,
		"%s Population Generator (Version %d.%d.%d%s)\n",
		get_str("PROG"), VERSION, RELEASE, MODIFICATION, PATCH);
	fprintf (stderr, "Copyright %s %s\n", COPYRIGHT, C_DATES);

	if (msg != NULL)
		printf("\nERROR: %s\n\n", msg);
	
	printf("\n\nUSAGE: %s [options]\n", get_str("PROG"));
	printf("\nNote: When defined in a parameter file (using -p), parmeters should\n");
	printf("use the form below. Each option can also be set from the command\n");
	printf("line, using a form of '%cparam [optional argument]'\n", 
		OPTION_START);
	printf("Unique anchored substrings of options are also recognized, and \n");
	printf("case is ignored, so '%csc' is equivalent to '%cSCALE'\n\n",
		OPTION_START, OPTION_START);
	printf("General Options\n===============\n");
	print_options(options, 0);
	printf("\n");
	printf("Advanced Options\n===============\n");
	print_options(options, 1);
	printf("\n");
	exit((msg == NULL)?0:1);


}

/*
 * Routine: set_option(int var, char *value)
 * Purpose: set a particular parameter; main entry point for the module
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
set_option(char *name, char *param)
{
	int res = 1;
	option_t *o;
	char parse_int[15];
	char *cp;

	init_params();
	
	res = fnd_param(name);
	if (res == -1)
		return(res);
	
	o = &options[res];

	if (o->flags & OPT_NOP)
	{
		printf("ERROR: Cannot accept %s.\tNot Implemented!\n", o->name);
		return(0);
	}

	/* option is already set from the command line or hard-coded */
	/* and doesn't allow multiple settings */

	switch(o->flags & TYPE_MASK)
	{
	case	OPT_FLG:
		if ((param && (*param == 'Y' || *param == 'Y' || *param == OPTION_START)) ||
			(param == NULL))
		{
			if (o->action)
				if (o->action(o->name, NULL) < 0)
					usage(o->name, "Cannot process option");
			set_flg(name);
		}
		else
			clr_flg(name);
		res = 1;
		break;
	case	OPT_INT:
		if (o->action)
      {
			if ((res = o->action(o->name, param)) < 0)
				usage(NULL, "Bad parameter argument");
			else
				sprintf(parse_int, "%d", res);
      }
		set_int(name, (o->action)?parse_int:param);
		res = 2;
		break;
	case	OPT_STR:
		if (*param == '"')
		{
			cp = strchr((param + 1), '"');
			if (cp == NULL)	/* non-terminated string literal */
				usage(NULL, "Non-terminated string");
			*cp = '\0';
			param += 1;
		}
		else
		{
			cp = strpbrk(param, " \t\n");
			if (cp != NULL)
				*cp = '\0';
		}
		if (o->action && strlen(param))
			if (o->action(o->name, param) < 0)
				usage(o->name, "Cannot process option");
		set_str(name, param);
		res = 2;
		break;
	default:
		fprintf(stderr, "Invalid option/type (%d/%s)\n", 
		    o->flags & TYPE_MASK, o->name);
		exit(0);
		break;
	}

	o->flags |= OPT_SET;	/* marked as set */

	return(res);
}

/*
 * Routine: process_options(int count, char **vector)
 * Purpose:  process a set of command line options
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: 20000309 need to return integer to allow processing of left-over args
 */
int
process_options (int count, char **vector)
{
	int option_num = 1,
	res = 1;

	init_params();

	while (option_num < count)
	{
		if (*vector[option_num] == OPTION_START)
		{
			if (option_num == (count - 1))
				res = set_option(vector[option_num] + 1, NULL);
			else
				res = set_option(vector[option_num] + 1, 
				    vector[option_num + 1]);
		}

		if (res < 0)
		{
			printf ("ERROR: option '%s' or its argument unknown.\n", 
			    (vector[option_num] + 1));
			usage (NULL, NULL);
			exit (1);
		}
		else
			option_num += res;
	}

#ifdef JMS
	if (is_set("VERBOSE"))
		print_params();
#endif

	return(option_num);
}

/*
 * Routine: read_file(char *param_name, char *fname)
 * Purpose: process a parameter file
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
read_file(char *param_name, char *optarg)
{
	FILE *fp;
	char *cp;
	char line[MAX_LINE_LEN];
	char name[100];
	int index;

	init_params();
	
	if ((fp = fopen(optarg, "r")) == NULL)
		return(-1);
	while (fgets(line, MAX_LINE_LEN, fp) != NULL)
	{
		if ((cp = strchr(line, '\n')) != NULL)
			*cp = '\0';
		if ((cp = strchr(line, '-')) != NULL)
			if (*(cp + 1) == '-')
				*cp = '\0';
		if ((cp = strtok(line, " \t=\n")) != NULL)
		{
			strcpy(name, cp);
			index = fnd_param(name);
			if (index == -1)
				continue;	/* JMS: errors are silently ignored */
			cp += strlen(cp) + 1;
			while ( *cp && strchr(" \t =", *cp))
				cp++;

			/* command line options over-ride those in a file */
			if (options[index].flags & OPT_SET)
				continue;

			if (*cp)
			{
				switch(options[index].flags & TYPE_MASK)
				{
				case OPT_INT:
					if ((cp = strtok(cp, " \t\n")) != NULL)
						set_option(name, cp);
					break;
				case OPT_STR:
				case OPT_FLG:
					set_option(name, cp);
					break;
				}
			}
		}
	}

	fclose(fp);

	return(0);
}

/*
 * Routine: print_params(void)
 * Purpose: print a parameter summary to display current settings
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
void 
print_params(void)
{
	int i;

	init_params();
	
	for (i=0; options[i].name != NULL; i++)
		if (options[i].name != NULL)
		{
			printf("%s = ", options[i].name);
			switch(options[i].flags & TYPE_MASK)
			{
			case OPT_INT:
				printf("%d\n", get_int(options[i].name));
				break;
			case OPT_STR:
				printf("%s\n", get_str(options[i].name));
				break;
			case OPT_FLG:
				printf("%c\n", is_set(options[i].name)?'Y':'N');
				break;
			}
		}

		return;
}

/*
 * Routine: fnd_param(char *name, int *type, char *value)
 * Purpose: traverse the defined parameters, looking for a match
 * Algorithm: 
 * Data Structures:
 *
 * Params:
 * Returns: index of option
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
fnd_param(char *name)
{
	int i,
		res = -1;

	for (i=0; options[i].name != NULL; i++)
	{
		if (strncasecmp(name, options[i].name, strlen(name)) == 0)
      {
			if (res == -1)
				res = i;
			else
				return(-1);
      }
	}

	return(res);
}

/*
 * Routine:  GetParamName(int nParam)
 * Purpose:  Translate between a parameter index and its name
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
char *
GetParamName(int nParam)
{
	init_params();
	
	return(options[nParam].name);
}


/*
 * Routine:  GetParamValue(int nParam)
 * Purpose:  Retrieve a parameters string value based on an index
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
char *
GetParamValue(int nParam)
{
	init_params();
	
	return(params[options[nParam].index]);
}

/*
 * Routine:  load_param(char *szValue, int nParam)
 * Purpose:  Set a parameter based on an index
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
load_param(int nParam, char *szValue)
{
	init_params();
	
	if (options[nParam].flags & OPT_SET)		/* already set from the command line */
		return(0);
	else
		strcpy(params[options[nParam].index], szValue);

	return(0);
}

/*
 * Routine:  IsIntParam(char *szValue, int nParam)
 * Purpose:  Boolean test for integer parameter
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
IsIntParam(char *szParam)
{
	int nParam;

	if ((nParam = fnd_param(szParam)) == -1)
		return(nParam);

	return((options[nParam].flags & OPT_INT)?1:0);
}

/*
 * Routine:  IsStrParam(char *szValue, int nParam)
 * Purpose:  Boolean test for string parameter
 * Algorithm:
 * Data Structures:
 *
 * Params:
 * Returns:
 * Called By: 
 * Calls: 
 * Assumptions:
 * Side Effects:
 * TODO: None
 */
int
IsStrParam(char *szParam)
{
	int nParam;

	if ((nParam = fnd_param(szParam)) == -1)
		return(nParam);

	return((options[nParam].flags & OPT_STR)?1:0);
}

#ifdef TEST

main()
{
	init_params();
	set_int("SCALE", "7");
	set_flg("VERBOSE");
	set_str("DISTRIBUTIONS", "'some file name'");
	print_params();
	set_int("s" , "8");
	clr_flg("VERBOSE");
	printf("DIST is %s\n", get_str("DISTRIBUTIONS"));
	print_params();
	usage(NULL, NULL);
}
#endif /* TEST_PARAMS */
